package main;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.StringTokenizer;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;

import entities.Lugar;
import entities.Teatro;

/**
 * 
 * @author Leonardo Lino Nº45340
 * @author Diogo Pires Nº40351
 */
public class FileOperator {

	private final String CURRENT_DIR;
	private final String RUNNING_LOCATION;
	private String baseLocation = "";
	private String eol = System.getProperty("line.separator");

	public FileOperator(String baseLocation) {
		CURRENT_DIR = System.getProperty("user.dir") + File.separator;

		if (SystemUtils.IS_OS_LINUX) {
			String temp = FileOperator.path(new File(this.getClass()
					.getProtectionDomain().getCodeSource().getLocation()
					.getPath()), true, true);
			RUNNING_LOCATION = temp.replace("%20", " ");
		} else if (SystemUtils.IS_OS_WINDOWS)
			RUNNING_LOCATION = CURRENT_DIR;
		else
			RUNNING_LOCATION = FileOperator.path(new File(this.getClass()
					.getProtectionDomain().getCodeSource().getLocation()
					.getPath()), true, true);

		this.baseLocation = baseLocation;
	}

	public boolean genTheater(int teatroID, int rows, int columns,
			boolean overwrite) throws IOException {

		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".txt");

		if (overwrite || !file.exists()) {
			verificaPath(file);

			FileWriter fileW = new FileWriter(file);
			BufferedWriter bufw = new BufferedWriter(fileW);

			try {
				bufw.write("Nome:" + "teatro" + teatroID);
				bufw.newLine();
				bufw.write("NumLugares:" + (rows * columns));
				bufw.newLine();
				bufw.write("NumLugaresOcupados:0");
				bufw.newLine();
				bufw.write("NumLugaresReservados:0");
				bufw.newLine();

				for (int i = 0; i < rows; i++) {
					bufw.write("-Row:" + i + " ");

					for (int j = 1; j <= columns; j++) {
						bufw.write(j + ":F ");
					}
					bufw.newLine();
				}
			} finally {
				bufw.close();
			}

		}
		return true;
	}

	public boolean deleteTheater(int teatroID) {
		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".txt");
		return file.delete();
	}

	public boolean writeTheater(Teatro teatro) throws IOException {
		boolean result = true;
		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatro.id + ".txt");
		File fileTemp = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatro.id + ".temp");

		verificaPath(file);

		FileWriter fileW = new FileWriter(fileTemp);
		BufferedWriter bufw = new BufferedWriter(fileW);

		try {
			bufw.write("Nome:" + "teatro" + teatro.id);
			bufw.newLine();
			bufw.write("NumLugares:" + teatro.lugaresTotal);
			bufw.newLine();
			bufw.write("NumLugaresOcupados:" + teatro.lugaresOcupados);
			bufw.newLine();
			bufw.write("NumLugaresReservados:" + teatro.lugaresReservados);

			int row = -1;

			for (int i = 0; i < teatro.lugares.size(); i++) {
				Lugar lugar = teatro.lugares.get(i);

				if (row != lugar.row) {
					bufw.newLine();
					row = lugar.row;
					bufw.write("-Row:" + row + " ");
				}

				switch (lugar.state) {
				case Lugar.STATE_FREE:
					bufw.write(lugar.column + ":F ");
					break;
				case Lugar.STATE_RESERVED:
					bufw.write(lugar.column + ":R:" + lugar.getUserID() + ":"
							+ lugar.getTimestamp() + " ");
					break;
				case Lugar.STATE_BOUGHT:
					bufw.write(lugar.column + ":B:" + lugar.getUserID() + " ");
					break;
				default:
					return false;
				}
			}
		} catch (Exception e) {
			result = false;
		} finally {
			bufw.close();

			if (result) {
				file.delete();
				fileTemp.renameTo(file);
			} else
				fileTemp.delete();
		}

		return true;
	}

	// metodo para pesquisar por um unico teatro
	public Teatro getTheater(int teatroID, int totalColumns) throws IOException {
		String everything = "";
		String[] lines;
		String nome = "";
		int numLugares = 0;
		int lugaresOcupados = 0;
		int lugaresReservados = 0;

		ArrayList<Lugar> lugares = new ArrayList<Lugar>();
		int linha = 0, coluna = 0, estado = 0;

		everything = fileToString(teatroID);

		lines = everything.split(eol);

		for (String str : lines) {
			if (str.startsWith("-")) {
				String args[] = str.split("\\s+");

				linha = Integer.parseInt(args[0].split(":")[1]);

				for (int i = 1; i < args.length; i++) {

					coluna = Integer.parseInt(args[i].split(":")[0]);

					switch (args[i].split(":")[1].charAt(0)) {

					case 'F':
						estado = Lugar.STATE_FREE;
						lugares.add(new Lugar(linha * totalColumns + coluna,
								linha, coluna, estado));
						break;

					case 'R': {
						estado = Lugar.STATE_RESERVED;
						int userID = Integer.parseInt(args[i].split(":")[2]);
						String timestamp = args[i].split(":")[3];
						Lugar lugar = new Lugar(linha * totalColumns + coluna,
								linha, coluna, estado);
						lugar.setUserID(userID);
						lugar.setTimestamp(timestamp);
						lugares.add(lugar);
						break;
					}

					case 'B': {
						estado = Lugar.STATE_BOUGHT;
						int userID = Integer.parseInt(args[i].split(":")[2]);
						Lugar lugar = new Lugar(linha * totalColumns + coluna,
								linha, coluna, estado);
						lugar.setUserID(userID);
						lugares.add(lugar);
						break;
					}

					}

				}
			} else {
				String args[] = str.split(":");

				if (args[0].equals("Nome")) {
					nome = args[1];
					continue;
				} else if (args[0].equals("NumLugares")) {
					numLugares = Integer.parseInt(args[1]);
					continue;
				} else if (args[0].equals("NumLugaresOcupados")) {
					lugaresOcupados = Integer.parseInt(args[1]);
					continue;
				} else if (args[0].equals("NumLugaresReservados")) {
					lugaresReservados = Integer.parseInt(args[1]);
				}
			}

		}

		Teatro theater = new Teatro(teatroID, numLugares, lugaresOcupados,
				lugaresReservados, nome, lugares);

		return theater;
	}

	// reserva de 1 lugar num teatro
	public boolean reserveSeat(int teatroID, int userID, int row, int col,
			String timestamp) throws IOException {
		String everything = fileToString(teatroID);
		String[] lines = everything.split(eol);
		boolean result = true;

		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".txt");
		File fileTemp = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".temp");

		verificaPath(file);

		FileWriter fileW = new FileWriter(fileTemp);
		BufferedWriter bufw = new BufferedWriter(fileW);

		try {
			for (String str : lines) {
				if (str.startsWith("-Row:" + row)) {

					String[] args = str.split("\\s+");

					for (int i = 0; i < args.length; i++) {
						if (args[i].startsWith(Integer.toString(col) + ":")) {
							String[] seatParams = args[i].split(":");

							switch (seatParams[1].charAt(0)) {
							case 'F':
								seatParams[1] = "R";

								String updatedSeatParams[] = new String[seatParams.length + 2];
								System.arraycopy(seatParams, 0,
										updatedSeatParams, 0, seatParams.length);
								seatParams = updatedSeatParams;
								seatParams[2] = Integer.toString(userID);
								seatParams[3] = timestamp;
								break;
							default:
								result = false;
								break;
							}

							for (int j = 0; j < seatParams.length; j++)
								if (j == seatParams.length - 1
										&& i == args.length - 1) {
									bufw.write(seatParams[j]);
									bufw.newLine();
								} else if (j == seatParams.length - 1)
									bufw.write(seatParams[j] + " ");
								else
									bufw.write(seatParams[j] + ":");

						} else if (i == args.length - 1) {
							bufw.write(args[i]);
							bufw.newLine();
						} else
							bufw.write(args[i] + " ");
					}

				} else {
					if (str.startsWith("NumLugaresReservados")) {
						String[] args = str.split(":");
						args[1] = Integer
								.toString(Integer.parseInt(args[1]) + 1);

						bufw.write(args[0] + ":" + args[1]);
						bufw.newLine();
					} else {
						bufw.write(str);
						bufw.newLine();
					}
				}
			}
		} catch (Exception e) {
			result = false;
		} finally {
			bufw.close();

			if (result) {
				file.delete();
				fileTemp.renameTo(file);
			} else
				fileTemp.delete();
		}

		return result;
	}

	public boolean removeSeatReserve(int teatroID, int userID, int row, int col)
			throws IOException {

		String everything = fileToString(teatroID);
		String[] lines = everything.split(eol);
		boolean result = true;

		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".txt");
		File fileTemp = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".temp");

		verificaPath(file);

		FileWriter fileW = new FileWriter(fileTemp);
		BufferedWriter bufw = new BufferedWriter(fileW);

		try {
			for (String str : lines) {
				if (str.startsWith("-Row:" + row)) {

					String[] args = str.split("\\s+");

					for (int i = 0; i < args.length; i++) {
						if (args[i].startsWith(Integer.toString(col) + ":")) {
							String[] seatParams = args[i].split(":");

							switch (seatParams[1].charAt(0)) {
							case 'R':
								if (Integer.parseInt(seatParams[2]) == userID) {
									seatParams[1] = "F";
									String updatedSeatParams[] = new String[seatParams.length - 2];
									System.arraycopy(seatParams, 0,
											updatedSeatParams, 0,
											seatParams.length - 2);
									seatParams = updatedSeatParams;
								} else
									result = false;
								break;
							default:
								result = false;
								break;
							}

							for (int j = 0; j < seatParams.length; j++)
								if (j == seatParams.length - 1
										&& i == args.length - 1) {
									bufw.write(seatParams[j]);
									bufw.newLine();
								} else if (j == seatParams.length - 1)
									bufw.write(seatParams[j] + " ");
								else
									bufw.write(seatParams[j] + ":");

						} else if (i == args.length - 1) {
							bufw.write(args[i]);
							bufw.newLine();
						} else
							bufw.write(args[i] + " ");
					}

				} else {

					if (str.startsWith("NumLugaresReservados")) {
						String[] args = str.split(":");
						args[1] = Integer
								.toString(Integer.parseInt(args[1]) - 1);

						bufw.write(args[0] + ":" + args[1]);
						bufw.newLine();
					} else {
						bufw.write(str);
						bufw.newLine();
					}
				}
			}
		} catch (Exception e) {
			result = false;
		} finally {
			bufw.close();

			if (result) {
				file.delete();
				fileTemp.renameTo(file);
			} else
				fileTemp.delete();
		}

		return result;
	}

	// compra de 1 lugar num teatro
	public boolean purchaseSeat(int teatroID, int userID, int row, int col)
			throws IOException {

		String everything = fileToString(teatroID);
		String[] lines = everything.split(eol);
		boolean result = true;
		boolean wasReserved = false;

		// ///////////////////////////
		ArrayList<String> header = new ArrayList<String>();
		StringBuilder sb = new StringBuilder();
		// ///////////////////////////

		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".txt");
		File fileTemp = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".temp");

		verificaPath(file);

		FileWriter fileW = new FileWriter(fileTemp);
		BufferedWriter bufw = new BufferedWriter(fileW);

		try {
			for (String str : lines) {
				if (str.startsWith("-Row:" + row)) {

					String[] args = str.split("\\s+");

					for (int i = 0; i < args.length; i++) {
						if (args[i].startsWith(Integer.toString(col) + ":")) {
							String[] seatParams = args[i].split(":");

							switch (seatParams[1].charAt(0)) {
							case 'F': {
								seatParams[1] = "B";

								String updatedSeatParams[] = new String[seatParams.length + 1];
								System.arraycopy(seatParams, 0,
										updatedSeatParams, 0, seatParams.length);
								seatParams = updatedSeatParams;
								seatParams[2] = Integer.toString(userID);
								break;
							}
							case 'R': {
								if (Integer.parseInt(seatParams[2]) == userID) {

									String updatedSeatParams[] = new String[seatParams.length - 1];
									System.arraycopy(seatParams, 0,
											updatedSeatParams, 0,
											seatParams.length - 1);

									seatParams = updatedSeatParams;
									seatParams[1] = "B";
									wasReserved = true;
								} else
									result = false;
								break;
							}
							default:
								result = false;
								break;
							}

							for (int j = 0; j < seatParams.length; j++)
								if (j == seatParams.length - 1
										&& i == args.length - 1) {
									sb.append(seatParams[j]);
									// sb.append(eol);
								} else if (j == seatParams.length - 1)
									sb.append(seatParams[j] + " ");
								else
									sb.append(seatParams[j] + ":");

						} else if (i == args.length - 1) {
							sb.append(args[i]);
							// sb.append(eol);
						} else
							sb.append(args[i] + " ");
					}

					header.add(sb.toString());
				} else {
					header.add(str);// ////////
				}
			}
			int found = 0;

			for (String str : header) {
				if (found < 2) {
					if (str.startsWith("NumLugaresOcupados")) {
						String[] args = str.split(":");
						args[1] = Integer
								.toString(Integer.parseInt(args[1]) + 1);

						bufw.write(args[0] + ":" + args[1]);
						bufw.newLine();
					} else if (str.startsWith("NumLugaresReservados")) {
						if (wasReserved) {
							String[] args = str.split(":");
							args[1] = Integer.toString(Integer
									.parseInt(args[1]) - 1);

							bufw.write(args[0] + ":" + args[1]);
							bufw.newLine();
						} else {
							bufw.write(str);
							bufw.newLine();
						}
					} else {
						bufw.write(str);
						bufw.newLine();
					}
				} else {
					bufw.write(str);
					bufw.newLine();
				}
			}
		} catch (Exception e) {
			result = false;
		} finally {
			bufw.close();

			if (result) {
				file.delete();
				fileTemp.renameTo(file);
			} else
				fileTemp.delete();
		}

		return result;
	}

	public String getTheaterName(int teatroID) throws IOException {

		String str;
		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".txt", "");

		FileReader fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);

		try {
			while ((str = br.readLine()) != null) {
				String[] args = str.split(":");
				if (args[0].equals("Nome"))
					return args[1];
			}
		} finally {
			br.close();
		}
		return null;
	}

	/**
	 * devolve path do file sem o nome deste
	 * 
	 * @param file
	 * @return str- path do file
	 */
	public static String path(File file, boolean withSeparator,
			boolean withInicialSeparator) {
		boolean flag = true;
		String str = "";
		StringTokenizer s = new StringTokenizer(file.getPath(), File.separator);

		while (s.hasMoreTokens()) {
			String aux = s.nextToken();
			if (!aux.equals(file.getName())) {
				if (flag) {
					if (withInicialSeparator)
						str += File.separator + aux;
					else
						str += aux;
					flag = false;
				} else
					str += File.separator + aux;
			} else if (withSeparator)
				str += File.separator;
		}
		return str;
	}

	/**
	 * verifica se um dado caminho existe. Senao existir cria o caminho
	 * 
	 * @param file
	 */
	public static void verificaPath(File file) {
		String str = "";

		if (SystemUtils.IS_OS_LINUX) {
			str = path(file, false, true);
		}
		if (SystemUtils.IS_OS_WINDOWS) {
			str = path(file, false, false);
		}

		if (!file.exists()) {
			new File(str).mkdirs();
		}

	}

	private String fileToString(int teatroID) throws IOException {
		String everything = "";

		File file = new File(RUNNING_LOCATION + baseLocation + "teatro"
				+ teatroID + ".txt", "");

		FileReader fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);

		try {
			StringBuilder sb = new StringBuilder();
			String line = br.readLine();

			while (line != null) {
				sb.append(line);
				sb.append(eol);
				line = br.readLine();
			}
			everything = sb.toString();
		} finally {
			br.close();
		}

		return everything;
	}
	
	public void purgeStorage() throws IOException {
		File f = new File(RUNNING_LOCATION + baseLocation);
		delete(f);
	}
	
	private void delete(File f) throws IOException {
		FileUtils.deleteDirectory(f);
	}
}
